/*
 在JS中有很多 “伪(类)xxx” 的概念
   @1 伪(类)数组
     + 对于数组来讲，它的结构特征：数字做为索引、索引从0开始逐级递增、具备length属性记录其长度
     + 每一个数组都是 Array 类的实例，其 __proto__ 指向 Array.prototype「所以其可以直接使用Array原型对象上的 push/pop/forEach... 等方法」
     + 伪数组在结构上和数组几乎一模一样，但是其 __proto__ 并不指向 Array.prototype「所以不能直接调用数组提供的方法」，常见的伪数组有：arguments、元素集合、节点集合...
   @2 伪(类)promise「或者 thenable」
     + 基于 new Promise 所创造出来的实例，被称为标准的 promise 实例，其拥有 状态 和 值，也可以调用 Promise.prototype 上的 then/catch/finally 这些方法！
     + 我们可以遵照 PromiseA+规范，自己创造一个同样具备 状态和值，也具备 then 方法的实例，此实例被称为“类promise 或 thenable”  
   ...
   在我们平时的开发中，我们往往会出现这样的需求：让伪数组调用数组的方法，去实现相应的功能，此时就需要我们对伪数组做一些特殊的处理，而这个过程，有人把其叫做 “鸭式辨型 / 鸭子类型”！
  ------------
  特殊的处理方案
    @1 把需要借用的方法，赋值给伪数组的私有属性
      原理：首先要保证伪数组可以访问到这个方法；然后把方法执行，让方法中的this变为要操作的伪数组；因为伪数组和数组的“结构几乎一模一样”，所以操作数组的那些代码，“对于伪数组也基本上都生效”；
      let obj = { 0: 10, 1: 20, length: 2 };
      obj.push(30) //报错：obj.push is not a function
      obj.push = Array.prototype.push;
      obj.push(30) //正常处理

    @2 基于上述原理的分析，我们只需要把数组的方法执行，“让方法中的this变为伪数组”，这样就相当于伪数组直接借用数组的方法，去实现相应的效果了！
      let obj = { 0: 10, 1: 20, length: 2 };
      Array.prototype.push.call(obj,30)
      [].push.call(obj,30)
      ----
      [].forEach.call(obj,(item,index)=>{
        ...
      })
      ...
    
    @3 其实我们还可以直接修改伪数组的原型指向，让其指向 Array.prototype，这样数组的所有方法，伪数组都可以直接调用了！！
      let obj = { 0: 10, 1: 20, length: 2 };
      // obj.__proto__=Array.prototype
      Object.setPrototypeOf(obj, Array.prototype);
    
    @4 把伪数组直接转换为数组后，再去进行相应的操作即可
      let obj = { 0: 10, 1: 20, length: 2 };
      let arr = Array.from(obj);
      arr = [...obj];
      arr = [].slice.call(obj,0);
      ...
 */

/* 
// 模拟 Array.prototype.push 方法：方法只要执行，方法中的this是谁，就是在处理和操作谁
Array.prototype.push = function push(val) {
  // this:要操作的数组 arr  
  // val:向数组默认追加的内容 40
  this[this.length] = val;
  this.length++;
  return this.length;
};
let arr = [10, 20, 30];
console.log(arr.push(40)); //4   arr=[10, 20, 30, 40] 
*/
/* 
let obj = {
  2: 3, //1
  3: 4, //2
  length: 2, //3 4
  push: Array.prototype.push
}
obj.push(1); //把数组原型上的push方法执行，让方法中的this指向obj，val值是1
// this[this.length] = val; -> obj[obj.length]=1 -> obj[2]=1
// this.length++; -> obj.length++
obj.push(2);
// this[this.length] = val; -> obj[obj.length]=2 -> obj[3]=2
// this.length++; -> obj.length++
console.log(obj); 
*/